package stuttgart.uni.locationtracker;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.ServerSocket;
import java.net.Socket;

public class LocationServer {

	private ServerSocket tcpSocket;
	
	private DatagramSocket positionUpdateSocket;
	private int positionUpdateMessageLength = 276; //TODO anpassen
	
	private JobList<Socket> tcpJobList;
	private JobList<DatagramPacket> positionUpdateJobList;
	
	private int tcpWorkerCount = 5;
	private int positionUpdateWorkerCount = 5;
	
	private int tcpPort = 50;
	private int udpPort = 51;
	
	public static final int REQUEST_CLIENT_UPDATE = 0;
	public static final int REQUEST_FRIENDLIST_UPDATE = 1;
	public static final int REQUEST_LOGOUT = 2;
	
	boolean running = true;
	
	LocationDatabase db;
	
	public LocationServer() {
		System.out.println("Server start");
		
		initSockets();
		initJobLists();
		db = new LocationDatabase();
		startWorkReceivers();
		startWorkers();
	}

	private void initSockets(){
		try {
			tcpSocket = new ServerSocket(tcpPort);
			positionUpdateSocket = new DatagramSocket(udpPort);
		} catch (IOException e) {
			System.out.println("Initialising Sockets failed. Stoping LocationServer.");
			System.exit(1);
		}	
	}
	
	private void initJobLists() {
		tcpJobList = new JobList<Socket>();
		positionUpdateJobList = new JobList<DatagramPacket>();
	}
	
	private void startWorkReceivers(){
		
		//tcpSocket
		new Thread(){
			public void run(){
				while(running){
					try {
						tcpJobList.add(tcpSocket.accept());
					} catch (IOException e) {
						System.out.println("Error in clientQuerySocket.accept()");
					}
				}
				try {
					tcpSocket.close();
				} catch (IOException e) {
					System.out.println("Error in clientQuerySocket.close()");
				}
			}
		}.start();
		
		
		
		//positionUpdateSocket
		new Thread(){
			public void run(){
				while(running){
					byte[] message = new byte[positionUpdateMessageLength];
					
					DatagramPacket p = new DatagramPacket(message, positionUpdateMessageLength);
					try {
						positionUpdateSocket.receive(p);
					} catch (IOException e) {
						System.out.println("Error in positionUpdateSocket.receive(p)");
					}
					positionUpdateJobList.add(p);
				}
				positionUpdateSocket.close();
			}
		}.start();
	}
	
	//Creates the Threadpool
	private void startWorkers(){
		
		//tcpWorkers
		for(int i=0;i<tcpWorkerCount;i++){
			new Thread(){
				
				public void run(){
					while(running){
						try {
							Socket s = tcpJobList.poll();
							ObjectOutputStream out = new ObjectOutputStream(s.getOutputStream());
							ObjectInputStream in = new ObjectInputStream(s.getInputStream());
							int requestCode = in.readInt();
							switch(requestCode){
								case REQUEST_CLIENT_UPDATE:
									out.writeObject(db.getClientPosition(in.readUTF()));
									out.flush();
									break;
									
								case REQUEST_FRIENDLIST_UPDATE:
									out.writeObject(db.getFriendList());
									out.flush();
									break;
									
								case REQUEST_LOGOUT:
									db.removeClient(in.readUTF());
									break;
									
								default:
									break;
							}
							
							out.close();
							in.close();
							s.close();
						} catch (InterruptedException e) {
							System.out.println("Error in tcpJobList.poll()");
						} catch (IOException e) {
							System.out.println("IOError tcpWorker");
						}
					}
				}
			}.start();
		}
		
		
		//positionUpdateWorkers
		for(int i=0;i<positionUpdateWorkerCount;i++){
			new Thread(){
				public void run(){
					while(running){
						try {
							DatagramPacket p = positionUpdateJobList.poll();
							TrackingData updateData = (TrackingData) ObjectArrayConverter.toObjectfromByteArray(p.getData());
							TrackingData data = new TrackingData(updateData.getId().trim(),
															updateData.getUsername().trim(),
															updateData.getTime(),
															updateData.getLatitude(),
															updateData.getLongitude(),
															updateData.getBearing(),
															updateData.getSpeed());
							db.updateClientPosition(data);
						} catch (InterruptedException e) {
							System.out.println("Error in positionUpdateJobList.poll()");
						}
					}
				}
			}.start();
		}
	}
	
	
	public static void main(String[] args) {
		new LocationServer();
	}

}
